home *** CD-ROM | disk | FTP | other *** search
- #!/usr/sbin/perl
- 'di ';
- 'ds 00 \\"';
- 'ig 00 ';
- #
- # $Id: counter.pl.m,v 1.3 1996/10/29 21:28:19 murphy Exp $
- # Copyright (C) 1994 Daniel F. Rich (drich@corp.sgi.com)
- #
- # This program is free software; you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation; either version 2, or (at your option)
- # any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # counter - increment a counter for WWW
- #
- # This program is its own manual page, Install in man and bin.
- #
- # To install:
- # 1. Change $defcounterfile below to point to the default file containing
- # the count, or run using the -f option. This is the file that will be
- # used if the counter URL is not found in the config file, or of the
- # config file can't be opened.
- #
- # 2. If you want to run the counter out of inetd, add the following lines
- # to the files specified (the port in the services file, 8987, will be
- # the port specified in the URL in step 5):
- # /etc/services:
- # counter 8987/tcp # WWW visitor counter
- # /etc/inetd.conf (or /usr/etc/inetd.conf)
- # counter stream tcp nowait www /www/bin/counter counter
- # The last three fields of the line above are the user to run the counter
- # under (*must* have write access to the file specified by
- # $defcounterfile), the path to the counter program, and the args to pass
- # to the program respectively. The first argument must be counter, and
- # any other arguments may be added on after that.
- #
- # 3. If you wish to have a counter for more than one document, you will
- # need to create a config file. By default, this is named
- # 'counter.conf' and is in the same directory as the counter script,
- # but you can override this with the -c option.
- #
- # 4. Test the script by running it from the command line. If everything
- # is correct, it should return an X11 bitmap containing the current
- # count. You will want to run it with the input redirected from
- # /dev/null, as it is expecting to see a document URL on standard input.
- # Ex. ./counter.pl < /dev/null
- # If you want to test it with a sample document name, just type something
- # like:
- # GET /counter.xbm HTTP/1.0
- # after running the script.
- #
- # 5. Now, to use this from your documents, just use a URL of the form:
- # http://your.www.host:8987/counter.xbm
- # where counter.xbm is replaced with the filenames in your config file.
- # If you only wish to count a single document, the URL above should
- # work fine.
- #
- # Written 3/8/94 by Dan Rich (drich@corp.sgi.com)
- # Based on C code written by Frans van Hoesel (hoesel@chem.rug.nl)
- #
- # $Log: counter.pl.m,v $
- # Revision 1.3 1996/10/29 21:28:19 murphy
- # changes necessary to support forum 96 CD
- #
- # Revision 1.2 1996/04/22 13:38:28 DTjanitor
- # change .www references to .www_6.0
- #
- # Revision 1.1 1996/02/15 19:47:54 dave
- # adding in collapsed cgi-bin{int,ext,cd} files into their .m new forms
- #
- % Revision 1.6 1995/03/31 22:36:17 dave
- % updating files to conform to v5.0 CD
- %
- # Revision 1.4 1995/03/11 05:22:19 dave
- # fixed broken uncommented comment
- #
- #
- # Revision 1.5 1995/01/09 19:05:37 drich
- # Fixed a bug in the version number code.
- #
- # Revision 1.4 1995/01/06 00:42:42 drich
- # Added -V flag, and documentation for the '-f' option.
- # Also cleaned up the code so it runs with perl5.
- #
- # Revision 1.3 1994/10/25 13:31:38 drich
- # Fixed a socket bug, and corrected the URL where the program is available.
- #
- # Revision 1.2 1994/10/18 20:13:29 drich
- # Added daemon support and additional command line flags.
- # Also added new documentation (man page!!)
- #
- # Revision 1.1 1994/04/18 14:48:00 drich
- # Initial revision
- #
-
- sub usage {
- print STDERR "usage: $0 [-dziV] [-p port] [-f defaultcountfile | -c configfile] [-C interval] [-D level]\n";
- exit 1;
- }
-
- $userName = $ENV{'USER'};
- print $userName;
- $userName = "/usr/people/guest";
-
-
- #####################################
- # Setup/Define Machine-Specific stuff
- # reference the envariable DOCUMENT_ROOT directly
- # contents equal to the equivalent-on-your-machine of everything *inside*
- # the # single, "'", quotes: '$DocumentRoot="/AbsPath/to/your/DocRoot"'
- # AND have it's path from the root directory of "/" be whatever makes
- # sense for you on your server machine--we originally put it in
- # /usr/tmp but this became unreliable since /usr/tmp/ IS "fair game"
- # for being completely cleaned out at any time.
- $DocumentRoot=$ENV{'DOCUMENT_ROOT'};
- $DocumentRoot="/usr/netsite-docs" unless $DocumentRoot;
-
-
- $defcounterfile = "$DocumentRoot/toolbox/www/misc/counter.txt"; # Default file containing the count
- $defport = 8987; # Default port to run on (standalone)
- ($dirname,$basename) = ($0 =~ /^(.*)\/([^\/]*)$/);
- if (! $basename) {
- $dirname=".";
- $basename=$0;
- }
- $configfile = "$dirname/counter.conf";
-
- sub debug_print {
- local($level,$message) = @_;
-
- return if (!defined($debug));
- return if ($level > $debug);
- $message =~ s/\n(.)/\nDEBUG:$level:$1/g if ($message =~ /\n./);
- if ($nocr && ($olevel == $level)) {
- print STDERR "$message" if ($level <= $debug);
- } else {
- print STDERR "\n" if ($nocr);
- print STDERR "DEBUG:$level:$message";
- }
- $olevel = $level;
- $nocr = $message !~ /\n/;
- }
-
- # Read in configuration file
- sub read_config {
- local($sig) = @_;
-
- if ($sig eq 'HUP') {
- &debug_print(1,"Caught a SIG $sig--reloading config file\n");
- &syslog('info',"Caught a SIG $sig--reloading config file") && defined($syslog);
- }
-
- @config=();
- if ( -f "$configfile" ) {
- &debug_print(1, "Reading config file: $configfile...");
- open(CONFIG,"<$configfile") || &syslog('err',"err opening $configfile: $!");
- while (<CONFIG>) {
- chop;
- s/#.*$//;
- next if (!$_);
- ($url,$counter,$options) = split(/\s+/,$_,3);
- $config{$url} = $counter;
- $options{$url} = $options;
- &debug_print(2," $url --> $counter");
- &debug_print(2," w/zeros") if ($options{$url} =~ /zero/);
- &debug_print(2," inverse") if ($options{$url} =~ /inverse/);
- &debug_print(2,"\n");
- }
- close(CONFIG);
- &debug_print(1, "done.\n");
- }
- $SIG{'HUP'} = 'read_config';
- }
-
- sub give_up {
- print STDERR "$_[0]\n";
- &syslog('warning',"$_[0]\n") if ($syslog);
- exit 1;
- }
-
- push(@INC,'/usr/local/lib/perl');
- require('getopts.pl') || &give_up("$0: can\'t do getopts.pl: $@");
- require('sys/file.ph') || &give_up("$0: can\'t do sys/file.ph: $@");
- require('sys/errno.ph') || &give_up("$0: can\'t do sys/errno.ph: $@");
- undef($syslog); # True if we can use syslog
- require('syslog.pl') && ($syslog = 1);
-
- if ( ! &Getopts('c:C:dD:f:ip:Vz') ) {
- &usage;
- }
- $checkpoint = $opt_C; # Checkpoint interval
- $daemon = $opt_d; # Run as a daemon? (standalone)
- $debug = $opt_D;
- # Counter file name is specified by -f or the config file (-c)
- $defcounterfile = $opt_f if ($opt_f);
- $definverse = $opt_i; # Invert bitmap if true
- $port = $opt_p ? $opt_p : $defport; # Port to run on (standalone)
- $defleading_zero = $opt_z; # Print leading zeros on count
-
- # Print version info
- $version="\$Revision: 1.3 $_";
- $version =~ s/\$Revision: 1.3 $/\1/; # Strip the RCS version
- if ($debug || $opt_V) {
- print STDERR "WWW counter, version $version\n\n";
-
- if (!$debug) {
- print STDERR "\$RCSfile: counter.pl.m,v $ \$Revision: 1.3 $ \$Date: 1996/10/29 21:28:19 $ \n\n";
-
- print STDERR "Copyright (C) 1994 Daniel F. Rich (drich\@corp.sgi.com)\n\n";
-
- print STDERR "This program is free software; you can redistribute it and/or modify\n";
- print STDERR "it under the terms of the GNU General Public License as published by\n";
- print STDERR "the Free Software Foundation; either version 2, or (at your option)\n";
- print STDERR "any later version.\n\n";
-
- print STDERR "This program is distributed in the hope that it will be useful,\n";
- print STDERR "but WITHOUT ANY WARRANTY; without even the implied warranty of\n";
- print STDERR "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n";
- print STDERR "GNU General Public License for more details.\n";
- }
- }
- exit if ($opt_V);
-
- # Check for valid arguments
- if ($checkpoint && ($checkpoint !~ /^\d+$/)) {
- &usage;
- }
- if ($port && ($port !~ /^\d+$/)) {
- &usage;
- }
-
- &debug_print(1, "We have syslog!!\n") if ($syslog);
- &openlog($basename,'cons,pid','daemon');
-
- &read_config(0);
-
- # Start up the daemon if needed
- if ($daemon) {
- if (!$debug) {
- # Fork off, so we are a true deamon!
- unless (fork) {
- unless (fork) {
- sleep 1 until getppid == 1;
- if ( -e "/dev/console" ) {
- close(STDERR);
- open(STDERR,"> /dev/console");
- }
- } else {
- exit 0;
- }
- } else {
- wait;
- exit 0;
- }
- }
-
- close(STDIN); # We will map this to our socket
- close(STDOUT);
-
- require('sys/socket.ph') || &give_up("$0: can\'t do sys/socket.ph: $@");
-
- $sockaddr = 'S n a4 x8';
- $proto = (getprotobyname('tcp'))[2];
- $this = pack($sockaddr, &AF_INET, $port, "\0\0\0\0");
- select(NS); $| = 1; select(stdout);
-
- socket(S, &AF_INET, &SOCK_STREAM, $proto) || &give_up("socket: $!");
- bind(S,$this) || &give_up("bind: $!");
- listen(S,5) || &give_up("connect: $!");
-
- select(S); $| = 1; select(stdout);
-
- &syslog('notice',"daemon started on port $port");
- }
-
- # bitmap for each digit
- @digits = (0x00,0x00,0x00,0x3c,0x66,0x66,0x66,0x66,
- 0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x30,0x38,0x30,0x30,0x30,
- 0x30,0x30,0x30,0x30,0x30,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x30,
- 0x18,0x0c,0x06,0x06,0x7e,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x3c,0x66,0x60,0x60,0x38,
- 0x60,0x60,0x60,0x66,0x3c,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x30,0x30,0x38,0x38,0x34,
- 0x34,0x32,0x7e,0x30,0x78,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x7e,0x06,0x06,0x06,0x3e,
- 0x60,0x60,0x60,0x66,0x3c,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x38,0x0c,0x06,0x06,0x3e,
- 0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x7e,0x66,0x60,0x60,0x30,
- 0x30,0x18,0x18,0x0c,0x0c,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x3c,0x66,0x66,0x66,0x3c,
- 0x66,0x66,0x66,0x66,0x3c,0x00,0x00,0x00,
- 0x00,0x00,0x00,0x3c,0x66,0x66,0x66,0x66,
- 0x7c,0x60,0x60,0x30,0x1c,0x00,0x00,0x00
- );
-
- $notdone = 1;
- while ($notdone) { # Setup loop for daemon
- # Wait for connection...
- if ($daemon) {
- &debug_print(1,"Waiting for connection...\n");
- while (!accept(NS,S)) { # Restart if EINTR
- &give_up("accept: $!") if ($! != &EINTR);
- }
- open(STDOUT,">&NS"); # Map STDIN and STDOUT to the socket
- open(STDIN,"<&NS");
- }
-
- # Get URL and set counterfile
- $line=<STDIN>;
- ($counterurl) = ($line =~ /GET (.*) HTTP/);
- $counterfile = defined($config{$counterurl}) ?
- $config{$counterurl} : $defcounterfile;
- $leading_zero = defined($options{$counterurl}) ?
- ($options{$counterurl} =~ /zero/) : $defleading_zero;
- $inverse = defined $options{$counterurl} ?
- ($options{$counterurl} =~ /inverse/) : $definverse;
- &debug_print(1,"url: $counterurl");
- &debug_print(1," w/zero") if ($leading_zero);
- &debug_print(1,"\n");
-
- # Read and increment the counter file
- while (-f "${counterfile}.lock") { sleep 1 }
- open(LOCK,">${counterfile}.lock");
- close(LOCK);
- # If counter file exists use it, otherwise create a new one
- if (-f "$counterfile" ) {
- open(COUNTERFILE,"<$counterfile") ||
- &give_up("$0: can\'t open $counterfile: $!\n");
- # flock(COUNTERFILE,&LOCK_EX) ||
- # &give_up("$0: can\'t lock $counterfile: $!\n");
- $text = <COUNTERFILE>;
- close(COUNTERFILE);
- } else {
- $text = '0';
- }
-
- $text++;
- $len = length($text) > 7 ? length($text) : 7;
- open(COUNTERFILE,">$counterfile") ||
- &give_up("$0: can\'t open $counterfile: $!\n");
- printf COUNTERFILE "%${len}u\n",$text;
- if ($leading_zero) {
- $text = sprintf("%0${len}u",$text);
- } else {
- $len = length($text);
- $text = sprintf("%${len}u",$text);
- }
- &debug_print(1,"count: $text\n");
- # flock(COUNTERFILE,&LOCK_UN);
- close(COUNTERFILE);
- if ($checkpoint && ($text % $checkpoint == 0)) {
- if ($syslog) {
- &syslog('info',"$counterurl checkpoint: $text");
- } else {
- open(CHECKFILE,">${counterfile}.check") ||
- warn "$0: can\'t open ${counterfile}.check: $!\n";
- print CHECKFILE "%0${len}u\n",$text;
- close(CHECKFILE);
- }
- }
- unlink("${counterfile}.lock");
-
- # Generate an X11 bitmap on STDOUT
- printf STDOUT "#define count_width %d\n#define count_height 16\n", $len*8;
- printf STDOUT "static char count_bits[] = {\n";
- for ($y=0; $y < 16; $y++) {
- for ($x=0; $x < $len; $x++) {
- $d = substr($text,$x,1) - '0';
- print STDOUT '0x';
- if ($inverse) {
- printf STDOUT "%1x",(($digits[($d * 16) + $y] >> 4) ^ 0xf) & 0xf;
- printf STDOUT "%1x",($digits[($d * 16) + $y] ^ 0xf) & 0xf;
- } else {
- printf STDOUT "%1x",($digits[($d * 16) + $y] >> 4) & 0xf;
- printf STDOUT "%1x",$digits[($d * 16) + $y] & 0xf;
- }
- if ($x < $len-1) {
- print STDOUT ',';
- }
- }
- if ($y==15) {
- print STDOUT '};';
- } else {
- print STDOUT ',';
- }
- print STDOUT "\n";
- }
- if ($daemon) {
- close(STDOUT);
- close(STDIN);
- close(NS);
- } else {
- undef($notdone);
- }
- }
-
- ################### BEGIN PERL/TROFF TRANSITION
- .00 ;
-
- 'di
- .nr nl 0-1
- .nr % 0
- '; __END__ ############## END PERL/TROFF TRANSITION
- .TH counter 1 "October 14, 1994"
- .SH NAME
- counter \- display a user counter for a World Wide Web document
- .SH SYNOPSIS
- .B counter
- [
- .I -dizV
- ] [
- .I -C interval
- ] [
- .I -D debuglevel
- ] [
- .I -f defaultcounterfile
- |
- .I -c configfile
- ] [
- .I -i
- ] [
- .I -p portnum
- ]
- .SH DESCRIPTION
- .B counter
- will display a count of users accessing a World Wide Web document.
- .LP
- The output of
- .B counter
- contains an x-bitmap of the current value stored in the counterfile.
- This can be used to count the number of accesses to a WWW document, by using
- the WWW browser's caching of image files (and therefore they
- won't request the image a second time).
- .LP
- .SH OPTIONS
- .TP
- .BI \-c file
- overrides the default config file \fIcounter.conf\fP. The config file
- specifies what file should be used to store the count for each URL that is to
- be counted. It contains one line for each counter, and each line is of the
- form:
- .nf
- .in +5
- .B URLfile counterfile options
- .in -5
- .fi
- \fBURLfile\fP contains just the filename from the counter bitmap URL (with a
- leading slash).
- The only \fBoptions\fP currently supported are \fIzero\fP and \fIinverse\fP.
- Ex.
- .nf
- .in +5
- .B /counter.xbm /www/counter.txt zero inverse
- .B /counter-home.xbm /www/counter-home.txt
- .in -5
- .fi
- .TP
- .BI \-C checkpoint
- causes \fBcounter\fP to checkpoint the count at the interval specified.
- This will be done using syslog if available, otherwise, a counterfile is
- created with \fI.count\fP appended to the name.
- .TP
- .B \-d
- will cause counter to detach itself from the controlling terminal and
- run as a network daemon on the port specified by the \fI-p\fP flag. This
- will also cause any error messages to be output to the console. If the
- \fI-D\fP option is also used, the program will not detach itself from the
- terminal, but will instead output the debugging information to stderr.
- .TP
- .BI \-D level
- enables debugging at the level specified.
- .TP
- .B \-f file
- specifies the counter file to use if either an unknown bitmap is specified,
- or if the config file cannot be opened.
- .TP
- .B \-i
- causes \fBcounter\fP to output an inverted bitmap (white numbers on a black
- background). This affects only the default counter if a config file is used.
- .TP
- .BI \-p port
- specifies the port number for the daemon to run on.
- .TP
- .B \-V
- will print the version number of the counter script and exit.
- .TP
- .B \-z
- forces \fBcounter\fP to print leading zeros on the counter. This affects only
- the default counter if a config file is used.
- .SH INSTALLATION
- First you need to decide if you want to run the counter from inetd, or as a
- standalone daemon. You can only run from inetd if you have the ability to
- edit system files and restart the inetd daemon. However, if you are going
- to run the counter with a config file, it will run faster if you run it as
- a standalone daemon (from inetd, it must reread the config file every
- time the counter is accessed).
- .LP
- If you are going to run it out of inetd, you need to change two of your
- network configuration files. You will need to add the following line to
- /etc/services (the 8987 is the port number that will be specified in the
- URL that accesses the counter):
- .nf
- .in +5
- .B counter 8987/tcp
- .in -5
- .fi
- and add the following to /etc/inetd.conf:
- .nf
- .in +5
- .B counter stream tcp nowait www /www/bin/counter counter
- .in -5
- .fi
- \fIWww\fP must be changed to a user who has write access to the counter files.
- The \fI/www/bin/counter\fP path needs to be changed to where you
- installed this program, and you can add any of the options above to the
- end of the line as they will be passed to the \fBcounter\fP script
- (NOTE: you cannot use \fI-p\fP or \fI-d\fP if running from inetd).
- .LP
- If you wish to use the counter in more than one document, you either need to
- run multiple copies of this script on different ports, or use a config file
- as specified above. By default, this file is named \fIcounter.conf\fP, and
- is located in the same directory as \fBcounter\fP. This can be overridden
- with the \fI-c\fP option.
- .LP
- At this point, you are ready to test the script by running it from the
- command line. You will need to redirect STDIN to /dev/null, as that is where
- \fBcounter\fP expects to see the document URL. It should return an X11
- bitmap of the current value in the default counter file. If you want to test
- \fBcounter\fP with one of the URLs in the config file, you need to pass the
- following as input to \fBcounter\fP (it will pause after you start it):
- .nf
- .in +5
- GET /counter.xbm HTTP/1.0
- .in -5
- .fi
- replacing /counter.xbm with the file you want to test from \fIcounter.conf\fP.
- .LP
- To get this count into your documents, use a URL of the form:
- .in +5
- .I http://your.server.host:8987/counter.xbm
- .in -5
- where \fI8987\fP is the port \fBcounter\fP is running on, and counter.xbm is
- one of the names in your config file.
- .SH FILES
- .TP
- .B counter.txt
- default file which holds the user count
- .TP
- .B counter.txt.lock
- default lock file used to enforce file locking
- .TP
- .B counter.conf
- default counter configuration file
- .TP
- .B /etc/inted.conf
- .TP
- .B /etc/services
- .SH SEE ALSO
- \fBsyslog\fP(3), \fBperl\fP.
- .SH BUGS
- .LP
- The one known problem with \fBcounter\fP is that it will occasionally
- zero the count file under heavy loads. I believe this is caused by a race
- condition with the lock files, but have never been able to track it down.
- That is the main reason the checkpoint option was added to this version
- of \fBcounter\fP.
- .SH CREDITS
- Frans van Hoesel (hoesel@chem.rug.nl) originaly wrote a simple
- C counter that I ported to perl. While his program was a perfectly
- good solution to the counting problem, I wanted a version written in
- perl that would be easier for me to maintain and modify.
- .LP
- Since then, it has had a slight case of creeping-featurism, and
- now contains several command line flags, a config file, and a man page.
- .SH AVAILABILITY
- The latest version of
- .B counter
- is available through SGI's Silicon Surf WWW site at
- .IR http://www.sgi.com/counter.html.
- Just select the counter for a description of the program, and an option
- to download the latest version of this script.
- .SH AUTHOR
- .I Daniel Rich\ \ \ \ <drich@corp.sgi.com>
-